﻿using System;
using System.Diagnostics.Contracts;
using AnthStat.Infrastructure.Statistics;

namespace AnthStat.Domain
{
    public sealed class ResultsWHO : IResults
    {
        public event EventHandler ScoresUpdated;

        public Measurement Measurement { get; private set; }

        public double? BMIForAgeZ { get; set; }
        public double? BMIForAgeP { get; set; }

        public double? HeadCircumferenceForAgeZ { get; set; }
        public double? HeadCircumferenceForAgeP { get; set; }

        public double? StatureForAgeZ { get; set; }
        public double? StatureForAgeP { get; set; }

        public double? WeightForStatureZ { get; set; }
        public double? WeightForStatureP { get; set; }

        public double? WeightForAgeZ { get; set; }
        public double? WeightForAgeP { get; set; }

        public double? SSFForAgeZ { get; set; }
        public double? SSFForAgeP { get; set; }

        public double? TSFForAgeZ { get; set; }
        public double? TSFForAgeP { get; set; }

        public double? MidUpperArmCircumferenceForAgeZ { get; set; }
        public double? MidUpperArmCircumferenceForAgeP { get; set; }

        public ResultsWHO(Measurement measurement)
        {
            Contract.Requires(measurement != null);

            this.Measurement = measurement;
        }

        public void CalculateScores()
        {
            // Get z-scores

            double ageDays = Measurement.AgeDays;
            double ageMonths = Measurement.AgeMonths;
            double? bmi = Measurement.BodyMassIndex;
            double? height = Measurement.Height;
            double? weight = Measurement.Weight;
            double? headCircumference = Measurement.HeadCircumference;

            double? ssf = Measurement.SubscapularSkinfold;
            double? tsf = Measurement.TricepsSkinfold;
            double? muac = Measurement.MidUpperArmCircumference;

            bool isRecumbent = Measurement.IsRecumbent;

            if (isRecumbent && ageMonths >= 24)
            {
                // remove 0.7 cm from height
                height = height - 0.7;
            }
            else if (!isRecumbent && ageMonths < 24)
            {
                // add 0.7 cm to height
                height = height + 0.7;
            }
            
            // flags
            double bmiFlag = 0;
            double haFlag = 0;
            double waFlag = 0;
            double whFlag = 0;
            double headCircumFlag = 0;
            double ssfFlag = 0;
            double tsfFlag = 0;
            double muacFlag = 0;

            int sex = 1; // male

            if(Measurement.Person.Sex.Equals(Sex.Female))
            {
                sex = 2; // female
            }

            if (ageMonths <= 60 && (Measurement.GetIndicatorSet() == IndicatorSet.WHO2006 || Measurement.GetIndicatorSet() == IndicatorSet.WHO20062007))
            {
                if (bmi.HasValue && !Double.IsNaN(bmi.Value))
                {
                    BMIForAgeZ = WHO2006.GetZScore(Indicator.BMIForAge, bmi.Value, ageMonths, sex, ref bmiFlag);
                }
                else
                {
                    BMIForAgeZ = null;
                }

                if (height.HasValue && !Double.IsNaN(height.Value) &&
                    ((height.Value >= 45 && height.Value <= 110 && ageMonths < 24) ||
                    (height.Value >= 65 && height.Value <= 120 && ageMonths >= 24 ))
                    )
                {
                    StatureForAgeZ = WHO2006.GetZScore(Indicator.HeightForAge, height.Value, ageMonths, sex, ref haFlag);
                }
                else
                {
                    StatureForAgeZ = null;
                }

                if (weight.HasValue && !Double.IsNaN(weight.Value))
                {
                    WeightForAgeZ = WHO2006.GetZScore(Indicator.WeightForAge, weight.Value, ageMonths, sex, ref waFlag);
                }
                else
                {
                    WeightForAgeZ = null;
                }

                if (weight.HasValue && !Double.IsNaN(weight.Value) && height.HasValue && !Double.IsNaN(height.Value) &&
                    ((height.Value >= 45 && height.Value <= 110 && ageMonths < 24) ||
                    (height.Value >= 65 && height.Value <= 120 && ageMonths >= 24))
                    )
                {
                    WeightForStatureZ = WHO2006.GetZScore(Indicator.WeightForHeight, weight.Value, height.Value, sex, ref whFlag);
                }
                else
                {
                    WeightForStatureZ = null;
                }

                if (headCircumference.HasValue && !Double.IsNaN(headCircumference.Value))
                {
                    HeadCircumferenceForAgeZ = WHO2006.GetZScore(Indicator.HCForAge, headCircumference.Value, ageMonths, sex, ref headCircumFlag);
                }
                else
                {
                    HeadCircumferenceForAgeZ = null;
                }

                if (ssf.HasValue && !Double.IsNaN(ssf.Value) && (ageMonths >= 3 && ageMonths <= 60))
                {
                    SSFForAgeZ = WHO2006.GetZScore(Indicator.SSFForAge, ssf.Value, ageMonths, sex, ref ssfFlag);
                }
                else
                {
                    SSFForAgeZ = null;
                }

                if (tsf.HasValue && !Double.IsNaN(tsf.Value) && (ageMonths >= 3 && ageMonths <= 60))
                {
                    TSFForAgeZ = WHO2006.GetZScore(Indicator.TSFForAge, tsf.Value, ageMonths, sex, ref tsfFlag);
                }
                else
                {
                    TSFForAgeZ = null;
                }

                if (muac.HasValue && !Double.IsNaN(muac.Value) && (ageMonths >= 3 && ageMonths <= 60))
                {
                    MidUpperArmCircumferenceForAgeZ = WHO2006.GetZScore(Indicator.ACForAge, muac.Value, ageMonths, sex, ref muacFlag);
                }
                else
                {
                    MidUpperArmCircumferenceForAgeZ = null;
                }
            }
            else if (ageMonths > 60 && (Measurement.GetIndicatorSet() == IndicatorSet.WHO2007 || Measurement.GetIndicatorSet() == IndicatorSet.WHO20062007))
            {
                if (bmi.HasValue && !Double.IsNaN(bmi.Value))
                {
                    BMIForAgeZ = WHO2007.GetZScore(Indicator.BMIForAge, bmi.Value, ageMonths, sex, ref bmiFlag);
                }
                else
                {
                    BMIForAgeZ = null;
                }

                if (height.HasValue && !Double.IsNaN(height.Value))
                {
                    StatureForAgeZ = WHO2007.GetZScore(Indicator.HeightForAge, height.Value, ageMonths, sex, ref haFlag);
                }
                else
                {
                    StatureForAgeZ = null;
                }

                if (weight.HasValue && !Double.IsNaN(weight.Value) && ageMonths <= 120)
                {
                    WeightForAgeZ = WHO2007.GetZScore(Indicator.WeightForAge, weight.Value, ageMonths, sex, ref waFlag);
                }
                else
                {
                    WeightForAgeZ = null;
                }
            }
            else
            {
                BMIForAgeZ = null;
                StatureForAgeZ = null;
                WeightForAgeZ = null;
                WeightForStatureZ = null;
                HeadCircumferenceForAgeZ = null;
                SSFForAgeZ = null;
                TSFForAgeZ = null;
                MidUpperArmCircumferenceForAgeZ = null;
            }

            // Get p-values from z-scores

            if (BMIForAgeZ.HasValue)
            {
                BMIForAgeP = PFromZ.GetPercentile(BMIForAgeZ.Value);
            }
            else
            {
                BMIForAgeP = null;
            }

            if (HeadCircumferenceForAgeZ.HasValue)
            {
                HeadCircumferenceForAgeP = PFromZ.GetPercentile(HeadCircumferenceForAgeZ.Value);
            }
            else
            {
                HeadCircumferenceForAgeP = null;
            }

            if (StatureForAgeZ.HasValue)
            {
                StatureForAgeP = PFromZ.GetPercentile(StatureForAgeZ.Value);
            }
            else
            {
                StatureForAgeP = null;
            }

            if (WeightForStatureZ.HasValue)
            {
                WeightForStatureP = PFromZ.GetPercentile(WeightForStatureZ.Value);
            }
            else
            {
                WeightForStatureP = null;
            }

            if (WeightForAgeZ.HasValue)
            {
                WeightForAgeP = PFromZ.GetPercentile(WeightForAgeZ.Value);
            }
            else
            {
                WeightForAgeP = null;
            }

            if (SSFForAgeZ.HasValue)
            {
                SSFForAgeP = PFromZ.GetPercentile(SSFForAgeZ.Value);
            }
            else
            {
                SSFForAgeP = null;
            }

            if (TSFForAgeZ.HasValue)
            {
                TSFForAgeP = PFromZ.GetPercentile(TSFForAgeZ.Value);
            }
            else
            {
                TSFForAgeP = null;
            }

            if (MidUpperArmCircumferenceForAgeZ.HasValue)
            {
                MidUpperArmCircumferenceForAgeP = PFromZ.GetPercentile(MidUpperArmCircumferenceForAgeZ.Value);
            }
            else
            {
                MidUpperArmCircumferenceForAgeP = null;
            }

            if (ScoresUpdated != null)
            {
                ScoresUpdated(this, new EventArgs());
            }
        }
    }
}